using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
using Roslyn.Compilers.CSharp;
using SymbolicComputation;
namespace Sym.Test
{
    /// <summary>
    /// Interaction logic for Test1.xaml
    /// </summary>
    public partial class TestTransforms : Window
    {
        //REPL.Eval eval = new REPL.Eval();
        public List<String> log = new List<string>();
        bool stopTesting = false;
        System.Threading.ThreadStart threadStart;
        System.Threading.Thread thread;
        int startEquationsCount;
        int uniquePermutationsFound;
        int startEquationCount=0;
        bool useParallelTestLoop = true;
        Int32 totalEquationsToTest = 100;
        Int32 totalLevelsInEquation = 2;
        RandomEquationGenerator reg = new RandomEquationGenerator();
        Random random = new Random();
        public enum enumTestTypes { Isolate, Equation }
        bool useExistingEquations=false;
        bool isTesting = false;
        enumTestTypes dropTestType;
        int failedIsolations = 0;
        int failedDoubleChecks = 0;
        System.Threading.CancellationTokenSource cancellation = new System.Threading.CancellationTokenSource();
        RoslynEval.ScriptEval eval = new RoslynEval.ScriptEval();

        public TestTransforms()
        {
            InitializeComponent();
            this.Closing += new System.ComponentModel.CancelEventHandler(Test1_Closing);
        }


        void Test1_Closing(object sender, System.ComponentModel.CancelEventArgs e)
        {
            if (thread != null)
            {
                thread.Abort();
            }
        }

        private void btnStop_Click(object sender, RoutedEventArgs e)
        {
            stopTesting = true;
            cancellation.Cancel();
        }

        private void btnTestEquations_Click(object sender, RoutedEventArgs e)
        {
            stopTesting = false;
            TestTransformations(enumTestTypes.Equation);
        }

        private void btnTestIsolations_Click(object sender, RoutedEventArgs e)
        {
            stopTesting = false;
            TestTransformations(enumTestTypes.Isolate);
        }

        public void TestTransformations(enumTestTypes testType)
        {
            eval = new RoslynEval.ScriptEval();
            if (isTesting == true)
            {
                return;
            }
            isTesting = true;
            dropTestType = testType;
            Double testNumber;
            if (Miscellaneous.IsNumeric(this.txtTotalRandomEquationsToGenerate.Text) == false)
            {
                MessageBox.Show("You have to enter a number in the Total Equations To Generate Box.  Calculation aborted");
                return;
            }
            testNumber = Convert.ToDouble(this.txtTotalRandomEquationsToGenerate.Text);
            if (testNumber < 1)
            {
                MessageBox.Show("You have to enter a valid number in the Total Equations To Generate Box.  Calculation aborted");
                return;
            }
            totalEquationsToTest = Convert.ToInt32(this.txtTotalRandomEquationsToGenerate.Text);
            this.textBox1.Text = "";
            if (Miscellaneous.IsNumeric(this.txtTotalLevelsInEquations.Text) == false)
            {
                MessageBox.Show("You have to enter a number in the Total Levels in Equation.  Calculation aborted");
                return;
            }
            testNumber = Convert.ToDouble(this.txtTotalLevelsInEquations.Text);
            if (testNumber < 1)
            {
                MessageBox.Show("You have to enter a valid number in the Total Levels in Equation.  Calculation aborted");
                return;
            }
            log = new List<string>();
            threadStart = new System.Threading.ThreadStart(TestTransformsStart);
            thread = new System.Threading.Thread(threadStart);
            thread.Priority = System.Threading.ThreadPriority.Highest;
            thread.SetApartmentState(System.Threading.ApartmentState.MTA);
            this.label3.Content = "Testing...";
            startEquationsCount = 0;
            uniquePermutationsFound = 0;
            totalLevelsInEquation = Convert.ToInt32(this.txtTotalLevelsInEquations.Text);
            thread.Start();
        }

        public void TestTransformsStart()
        {
            startEquationCount =0;
            failedIsolations = 0;
            failedDoubleChecks = 0;
            if (useParallelTestLoop == true)
            {
                System.Threading.Tasks.ParallelOptions po = new System.Threading.Tasks.ParallelOptions();
                po.MaxDegreeOfParallelism = System.Environment.ProcessorCount;
                cancellation = new System.Threading.CancellationTokenSource();
                po.CancellationToken = cancellation.Token;
                try
                {
                    System.Threading.Tasks.Parallel.For(0, totalEquationsToTest, po, L =>
                    {
                        EquationLoopNest();
                        //po.CancellationToken.ThrowIfCancellationRequested();
                    });
                }
                catch (OperationCanceledException e)
                {
                    if (e == null)
                    {
                    }
                }
            }
            else
            {
                Int32 L;
                for (L = 0; L < totalEquationsToTest; L++)
                {
                    EquationLoopNest();
                    if (stopTesting == true)
                    {
                        break;
                    }
                }
            }
            this.Dispatcher.Invoke(new Action(delegate { this.label3.Content = "Done..."; }));
            GC.Collect();
            if (useExistingEquations ==false)
            {
                if (log.Count > 0)
                {
                    string str = Miscellaneous.Join(log, Environment.NewLine);
                    this.Dispatcher.Invoke(new Action(delegate { this.textBox1.Text = str; }));
                }
            }
            if (dropTestType == enumTestTypes.Isolate)
            {
                String textAddition = "Failed isolations=" + Convert.ToString(failedIsolations) + Environment.NewLine;
                textAddition += "Failed double checks=" + Convert.ToString(failedDoubleChecks) + Environment.NewLine;
                this.Dispatcher.Invoke(new Action(delegate { this.textBox1.Text += textAddition; }));
            }
            eval = null;
            isTesting = false;
        }

        public void EquationLoopNest()
        {
            if (dropTestType == enumTestTypes.Equation)
            {
                EquationLoopNestTransforms();
            }
            else if (dropTestType == enumTestTypes.Isolate)
            {
                EquationLoopNestIsolate();
            }
            GC.Collect();
        }

        IsolateSingleVariable isolate = new IsolateSingleVariable();
        public void EquationLoopNestIsolate()
        {
            startEquationCount++;
            String startEquation;
            startEquation = reg.CreateExpression(totalLevelsInEquation, RandomEquationGenerator.enumExpressionType.EqualSign);
            startEquationsCount++;
            List<String> childSidesOfequation = Miscellaneous.Split(startEquation, "==").ToList();
            String rhs = childSidesOfequation[1];
            SyntaxNode root = SymbolicComputation.Miscellaneous.EquationToSyntaxNode(rhs);
            List<SyntaxNode> nodes = root.DescendentNodesAndSelf().ToList();
            var numbers = from node in nodes
                          where node.Kind == SyntaxKind.NumericLiteralExpression
                          select node;
            nodes = numbers.ToList();
            int nodeIndex = random.Next(nodes.Count);
            SyntaxNode selectedNode = nodes[nodeIndex];
            String variableBeingSolvedFor = selectedNode.ToString();
            String result = SymbolicComputation.IsolateSingleVariable.IsolateEquation(startEquation,variableBeingSolvedFor);
            //String result = isolate.Isolate(startEquation, variableBeingSolvedFor);    
            String textAddition=startEquation+ "   " + variableBeingSolvedFor + Environment.NewLine;
            textAddition+=result+Environment.NewLine;
            bool lineIsBad=false;
            Double lhsVal=0;
            Double rhsVal=0;
            List<String> childSidesOfResult = Miscellaneous.Split(result, "==").ToList();
            if (childSidesOfResult.Count != 2)
            {
                lineIsBad = true;
            }
            else
            {
                try
                {
                    lhsVal = eval.Eval(childSidesOfResult[0]);
                    rhsVal = eval.Eval(childSidesOfResult[1]);
                }
                catch
                {
                    lineIsBad = true;
                }
            }
            textAddition += lhsVal.ToString() + "==" + rhsVal.ToString() + Environment.NewLine;

            String lhsResult = childSidesOfResult[0];
            if (lhsResult == variableBeingSolvedFor)
            {
                textAddition += "isolated" + Environment.NewLine;
            }
            else
            {
                textAddition += "failed to isolate" + Environment.NewLine;
                failedIsolations++;
            }
            if (Math.Abs(lhsVal  -rhsVal) <.00001)
            {
                textAddition += "checked both sides succeeded" + Environment.NewLine;
            }
            else
            {
                textAddition += "checked both sides failed" + Environment.NewLine;
                failedDoubleChecks++;
            }
            //textAddition = "";
            textAddition += Environment.NewLine;
            this.Dispatcher.Invoke(new Action(delegate { this.textBox1.Text += textAddition; }));
            textAddition = "";
            textAddition += "Equations generated =" + startEquationCount.ToString() + "     Failed isolations=" + Convert.ToString(failedIsolations);
            textAddition += "      Failed double checks=" + Convert.ToString(failedDoubleChecks);
            this.Dispatcher.Invoke(new Action(delegate { label2.Content = textAddition; }));
            if (lineIsBad == true)
            {
                //add error logging...
            }
            
        }

        public void EquationLoopNestTransforms()
        {
            startEquationCount++;
            ArtificialIntelligence.AI ai;
            Int32 L;
            String[] childSidesOfequation;
            String[] parentSidesOfEquation;
            Double lhsVal = 0;
            Double rhsVal = 0;
            ArtificialIntelligence.Job job;
            bool lineIsBad=false;
            bool startEquationCausesFailure = false;
            SymbolicComputation.Transform transform;
            RoslynEval.ScriptEval eval = new RoslynEval.ScriptEval();
            bool evaluated = false;
            String startEquation = null;
            startEquation = reg.CreateExpression(totalLevelsInEquation, RandomEquationGenerator.enumExpressionType.EqualSign);
            startEquationsCount++;
            String[] transformStrings = TransformSets.AllAsList().ToArray();
            TransformBranchFunctions.Combinatorize(startEquation, transformStrings, out ai);
            String[] reports = new String[ai.jobStates.Count];
            for (L = 0; L < reports.Length; L++)
            {
                lineIsBad = false;
                evaluated = false;
                uniquePermutationsFound++;
                job = ai.jobStates[L];
                if (job.parent != null)
                {
                    reports[L] += "parent=" + job.parent.jobState.ToString();
                }
                if (job.recentHistory != null)
                {
                    transform = job.recentHistory.transform;
                    reports[L] += "   " + SymbolicComputation.Transform.TransformToString(transform);
                }
                reports[L] += "   child=" + job.jobState.ToString();

                childSidesOfequation = Miscellaneous.Split(job.jobState.ToString(), "==").ToArray();
                if (childSidesOfequation.Length != 2)
                {
                    lineIsBad = true;
                }
                else
                {
                    try
                    {
                        lhsVal = eval.Eval(childSidesOfequation[0]);
                        rhsVal = eval.Eval(childSidesOfequation[1]);
                        evaluated = true;
                    }
                    catch
                    {
                        lineIsBad = true;
                    }
                    if (evaluated == true)
                    {
                        reports[L] += "   " + lhsVal.ToString() + "==" + rhsVal.ToString();
                        if (Math.Abs(lhsVal - rhsVal) > .0000001)
                        {
                            lineIsBad = true;
                            if (job.parent != null)
                            {
                                parentSidesOfEquation = Miscellaneous.Split(job.parent.jobState.ToString(), "==").ToArray();
                                try
                                {
                                    lhsVal = eval.Eval(parentSidesOfEquation[0]);
                                    rhsVal = eval.Eval(parentSidesOfEquation[1]);
                                }
                                catch
                                {
                                    lineIsBad = true;
                                }
                            }
                            if (Math.Abs(lhsVal - rhsVal) < .0000001)
                            {
                                lineIsBad = true;
                            }
                        }
                    }
                    if (lineIsBad == true)
                    {
                        log.Add(reports[L]);
                        this.Dispatcher.Invoke(new Action(delegate { this.label3.Content = "Testing..., failed equation(s) found"; }));
                        startEquationCausesFailure = true;
                    }
                }
            }
            if (startEquationCausesFailure == true)
            {
                log.Add("StartEquation=" + startEquation);
            }
            this.Dispatcher.Invoke(new Action(delegate { this.label2.Content = "Unique Permutations Tested for Equality = " + uniquePermutationsFound.ToString(); }));
            String report = Miscellaneous.Join(reports.ToList(), Environment.NewLine);
            report = "Start Equation=" + startEquation + Environment.NewLine + report;
            this.Dispatcher.Invoke(new Action(delegate { this.textBox1.Text = report; }));
            this.Dispatcher.Invoke(new Action(delegate { this.label1.Content = "Start Equations Tested = " + Convert.ToString(startEquationsCount); }));
        }

        private void btnClearTextBox_Click(object sender, RoutedEventArgs e)
        {
            this.textBox1.Text = "";
        }

        private void btnCopy_Click(object sender, RoutedEventArgs e)
        {
            Clipboard.SetText(this.textBox1.Text);
        }



    }
}